package mongo

import (
	"github.com/pkg/errors"
	"gopkg.in/mgo.v2"
	"log"
	"time"
	"ccGame/common"
)

type DBSession struct {
	dbSession  *mgo.Session
	database   *mgo.Database
	Collection *mgo.Collection
}

var MongoDB  *DBSession

func init() {
	dialInfo := &mgo.DialInfo{
		Addrs:   []string{common.MongoConf.Host+":"+common.MongoConf.Port},
		Timeout: 60 * time.Second,
		//Source:  authdb,
		//Username:  authuser,
		//Password:  authpass,
		PoolLimit: 4096,
	}

	s, err := mgo.DialWithInfo(dialInfo)
	if err != nil {
		log.Fatalf("Create Session: %s\n", err)
	}
	s.SetMode(mgo.Monotonic, true)
	db := &DBSession{}
	db.dbSession = s
	db.database  = s.DB(common.MongoConf.DbName)
	MongoDB = db
}


// 获得 新session
// 每一次操作都copy一份 Session,避免每次创建Session,导致连接数量超过设置的最大值阻塞新的请求
func (DB *DBSession) GetNewSession() (session *mgo.Session, err error) {
	if DB.dbSession == nil {
		err =  errors.New("dbSession is undefined !")
	}
	session = DB.dbSession.Copy()
	return
}

// 连接到mongoDB
// ** 每次链接会返回一个新的session会话，必须在后期调用Close关闭会话，否则会话将持续
func (DB *DBSession) connect(collection string) ( session *mgo.Session, c *mgo.Collection , err error) {
	session , err  = DB.GetNewSession()
	c = DB.database.C(collection)
	return
}

// 切换数据库 返回新的数据库连接
func (DB *DBSession) getDb (db string) (*mgo.Session, *mgo.Database) {
	session := DB.dbSession.Copy()
	return session, session.DB(db)
}

//func IsEmpty(db, collection string) bool {
//	ms, c := connect(db, collection)
//	defer ms.Close()
//	count, err := c.Count()
//	if err != nil {
//		log.Fatal(err)
//	}
//	return count == 0
//}

// 统计 满足条件的所有结果
func Count(collection string, query interface{}) (int, error) {
	session, c ,err := MongoDB.connect(collection)
	if err != nil {
		return 0,err
	}
	defer session.Close()
	return c.Find(query).Count()
}

// 插入一个或者多个文档
func Insert(collection string, docs ...interface{}) error {
	session, c ,err:= MongoDB.connect(collection)
	if err != nil {
		return err
	}
	defer session.Close()

	return c.Insert(docs...)
}

// 查询一条数据
func FindOne(collection string, query, selector,result interface{},sort ...interface{}) error { //,sort string
	session, c ,err:= MongoDB.connect(collection)
	if err != nil {
		return err
	}
	defer session.Close()

	if len(sort) > 0 {
		if s, ok := sort[0].(string); ok {
			return c.Find(query).Select(selector).Sort(s).One(result)
		}
	}
	return c.Find(query).Select(selector).One(result)
}

// 查询所有数据可限制条数
func FindAll(collection string, query, selector, result interface{},limit int , sort ...string) error {
	session, c ,err:= MongoDB.connect(collection)
	if err != nil {
		return err
	}
	defer session.Close()

	var s string
	if len(sort) >0 {
		for _,v := range sort{
			s +=v
		}
		return c.Find(query).Select(selector).Limit(limit).Sort(s).All(result)
	}
	return c.Find(query).Select(selector).Limit(limit).All(result)
}

// 查询结果分页处理
func FindPage(collection string, page, limit int, query, selector,sort, result  interface{}) error {
	session, c ,err:= MongoDB.connect(collection)
	if err != nil {
		return err
	}
	defer session.Close()
	var str string
	if arr  ,ok := sort.([]string); ok {
		for _,v := range arr {
			str += v
		}
	}
	return c.Find(query).Select(selector).Skip(page * limit).Limit(limit).Sort(str).All(result)
}

// 查询并返回满足条件的 cursor
func FindIter(collection string, query interface{}) *mgo.Iter {
	session, c ,err:= MongoDB.connect(collection)
	if err != nil {
		return nil
	}
	defer session.Close()
	return c.Find(query).Iter()
}
// 更新数据
func Update(collection string, selector, update interface{}) error {
	session, c ,err:= MongoDB.connect(collection)
	if err != nil {
		return err
	}
	defer session.Close()

	return c.Update(selector, update)
}

// 更新数据，若将要更新的数据不存在则将文档插入数据库
func Upsert(collection string, selector, update interface{}) error {
	session, c ,err:= MongoDB.connect(collection)
	if err != nil {
		return err
	}
	defer session.Close()

	_, err = c.Upsert(selector, update)
	return err
}

// 更新所有文档
func UpdateAll( collection string, selector, update interface{}) error {
	session, c ,err:= MongoDB.connect(collection)
	if err != nil {
		return err
	}
	defer session.Close()
	_, err = c.UpdateAll(selector, update)
	return err
}

// 移除一条符合件的数据
func Remove(collection string, selector interface{}) error {
	session, c ,err:= MongoDB.connect(collection)
	if err != nil {
		return err
	}
	defer session.Close()

	return c.Remove(selector)
}

// 移除所有符合条件的数据
func RemoveAll( collection string, selector interface{}) error {
	session, c ,err:= MongoDB.connect(collection)
	if err != nil {
		return err
	}
	defer session.Close()
	_, err = c.RemoveAll(selector)
	return err
}

// 插入一个或者多个文档
func BulkInsert( collection string, docs ...interface{}) (*mgo.BulkResult, error) {
	session, c ,err:= MongoDB.connect(collection)
	if err != nil {
		return nil , err
	}
	defer session.Close()

	bulk := c.Bulk()
	bulk.Insert(docs...)
	return bulk.Run()
}

// 批量删除
func BulkRemove(collection string, selector ...interface{}) (*mgo.BulkResult, error) {
	session, c ,err:= MongoDB.connect(collection)
	if err != nil {
		return nil , err
	}
	defer session.Close()

	bulk := c.Bulk()
	bulk.Remove(selector...)
	return bulk.Run()
}
// 批量删除所有
func BulkRemoveAll(db, collection string, selector ...interface{}) (*mgo.BulkResult, error) {
	session, c ,err:= MongoDB.connect(collection)
	if err != nil {
		return nil , err
	}
	defer session.Close()

	bulk := c.Bulk()
	bulk.RemoveAll(selector...)
	return bulk.Run()
}
// 批量更新
func BulkUpdate(db, collection string, pairs ...interface{}) (*mgo.BulkResult, error) {
	session, c ,err:= MongoDB.connect(collection)
	if err != nil {
		return nil , err
	}
	defer session.Close()

	bulk := c.Bulk()
	bulk.Update(pairs...)
	return bulk.Run()
}
// 更新所有
func BulkUpdateAll(db, collection string, pairs ...interface{}) (*mgo.BulkResult, error) {
	session, c ,err:= MongoDB.connect(collection)
	if err != nil {
		return nil , err
	}
	defer session.Close()

	bulk := c.Bulk()
	bulk.UpdateAll(pairs...)
	return bulk.Run()
}
// 批量插入数据
func BulkUpsert(db, collection string, pairs ...interface{}) (*mgo.BulkResult, error) {
	session, c ,err:= MongoDB.connect(collection)
	if err != nil {
		return nil , err
	}
	defer session.Close()

	bulk := c.Bulk()
	bulk.Upsert(pairs...)
	return bulk.Run()
}
// 聚合操作查找所有的
func PipeAll(db, collection string, pipeline, result interface{}, allowDiskUse bool) error {
	session, c ,err:= MongoDB.connect(collection)
	if err != nil {
		return err
	}
	defer session.Close()

	var pipe *mgo.Pipe
	if allowDiskUse {
		pipe = c.Pipe(pipeline).AllowDiskUse()
	} else {
		pipe = c.Pipe(pipeline)
	}
	return pipe.All(result)
}
// 聚合操作查找一个
func PipeOne(db, collection string, pipeline, result interface{}, allowDiskUse bool) error {
	session, c ,err:= MongoDB.connect(collection)
	if err != nil {
		return err
	}
	defer session.Close()

	var pipe *mgo.Pipe
	if allowDiskUse {
		pipe = c.Pipe(pipeline).AllowDiskUse()
	} else {
		pipe = c.Pipe(pipeline)
	}
	return pipe.One(result)
}
// 聚合操作查找cursor
func PipeIter(db, collection string, pipeline interface{}, allowDiskUse bool) *mgo.Iter {
	session, c ,err:= MongoDB.connect(collection)
	if err != nil {
		return nil
	}
	defer session.Close()

	var pipe *mgo.Pipe
	if allowDiskUse {
		pipe = c.Pipe(pipeline).AllowDiskUse()
	} else {
		pipe = c.Pipe(pipeline)
	}

	return pipe.Iter()

}

func Explain(db, collection string, pipeline, result interface{}) error {
	session, c ,err:= MongoDB.connect(collection)
	if err != nil {
		return err
	}
	defer session.Close()

	pipe := c.Pipe(pipeline)
	return pipe.Explain(result)
}

func GridFSCreate(databases, prefix, name string) (*mgo.GridFile, error) {
	session, db := MongoDB.getDb(databases)
	defer session.Close()
	gridFs := db.GridFS(prefix)
	return gridFs.Create(name)
}

func GridFSFindOne(databases, prefix string, query, result interface{}) error {
	session, d := MongoDB.getDb(databases)
	defer session.Close()
	gridFs := d.GridFS(prefix)
	return gridFs.Find(query).One(result)
}

func GridFSFindAll(databases, prefix string, query, result interface{}) error {
	session, db := MongoDB.getDb(databases)
	defer session.Close()
	gridFs := db.GridFS(prefix)
	return gridFs.Find(query).All(result)
}

func GridFSOpen(databases, prefix, name string) (*mgo.GridFile, error) {
	session, db := MongoDB.getDb(databases)
	defer session.Close()
	gridFs := db.GridFS(prefix)
	return gridFs.Open(name)
}

func GridFSRemove(databases, prefix, name string) error {
	session, db := MongoDB.getDb(databases)
	defer session.Close()
	gridFs := db.GridFS(prefix)
	return gridFs.Remove(name)
}
